package org.hismig.migration.executor;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

import javax.sql.DataSource;

import org.hismig.cfg.DataBase;
import org.hismig.migration.Context;
import org.hismig.migration.executor.ext.IExtExecutor;
import org.hismig.migration.parambuilder.IParamBuilder;
import org.hismig.migration.parambuilder.ParamBuildException;
import org.hismig.migration.result.DBResultInfo;
import org.hismig.migration.result.TableResultInfo;
import org.hismig.partition.IDynamicPartition;

/**
 * @declare: //TODO <br />
 *           This class created by Vansion.Hopihit on 2012-07-04 <br />
 *           <a href="mailto:vansionhopihit@gmail.com">Contact Me</a>
 * @version 1.0
 * @author Vansion.Hopihit
 * 
 */
public class DBExecutor extends DBExecutorBase {

	private List<IParamBuilder> paramBuilders;
	private IExtExecutor sourcePreExtExecutor;
	private IExtExecutor distPreExtExecutor;
	private IExtExecutor distAfterExtExecutor;
	private IExtExecutor sourceAfterExtExecutor;

	private DataSource sourceDataSource;
	private DataSource distDataSource;
	private IDynamicPartition dynamicPartition;

	private List<TableExecutor> tableExecutors;

	private DataBase dataBase;

	private List<IDBExecutorListener> dbExecutorListeners = new ArrayList<IDBExecutorListener>();

	public DataBase getDataBase() {
		return dataBase;
	}

	public void setDataBase(DataBase dataBase) {
		this.dataBase = dataBase;
	}

	public List<IParamBuilder> getParamBuilders() {
		return paramBuilders;
	}

	public void setParamBuilders(List<IParamBuilder> paramBuilders) {
		this.paramBuilders = paramBuilders;
	}

	public IDynamicPartition getDynamicPartition() {
		return dynamicPartition;
	}

	public void setDynamicPartition(IDynamicPartition dynamicPartition) {
		this.dynamicPartition = dynamicPartition;
	}

	public IExtExecutor getSourcePreExtExecutor() {
		return sourcePreExtExecutor;
	}

	public void setSourcePreExtExecutor(IExtExecutor sourcePreExtExecutor) {
		this.sourcePreExtExecutor = sourcePreExtExecutor;
	}

	public IExtExecutor getDistPreExtExecutor() {
		return distPreExtExecutor;
	}

	public void setDistPreExtExecutor(IExtExecutor distPreExtExecutor) {
		this.distPreExtExecutor = distPreExtExecutor;
	}

	public IExtExecutor getDistAfterExtExecutor() {
		return distAfterExtExecutor;
	}

	public void setDistAfterExtExecutor(IExtExecutor distAfterExtExecutor) {
		this.distAfterExtExecutor = distAfterExtExecutor;
	}

	public IExtExecutor getSourceAfterExtExecutor() {
		return sourceAfterExtExecutor;
	}

	public void setSourceAfterExtExecutor(IExtExecutor sourceAfterExtExecutor) {
		this.sourceAfterExtExecutor = sourceAfterExtExecutor;
	}

	public DBResultInfo execute(Context context) {

		try {
			for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
				dbExecutorListener.beforeDBExecuteStart(context, this);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		DBResultInfo dbResultInfo = new DBResultInfo(dataBase.getName());

		dbResultInfo.setStartTime(System.currentTimeMillis());
		for (IParamBuilder paramBuilder : paramBuilders) {
			try {
				paramBuilder.paramBuild(context);
			} catch (ParamBuildException e) {
				dbResultInfo.addException(e);
				dbResultInfo.setEndTime(System.currentTimeMillis());
				return dbResultInfo;
			}
		}

		if (null != sourcePreExtExecutor) {

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.beforeExtExecuteStart(context,
							sourcePreExtExecutor);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			try {
				dbResultInfo.setSourcePreResultInfo(sourcePreExtExecutor
						.execute(context));
			} catch (ExecutionException e) {
				dbResultInfo.addException(e);
				dbResultInfo.setEndTime(System.currentTimeMillis());
				return dbResultInfo;
			}

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.afterExtExecuteEnd(dbResultInfo
							.getSourcePreResultInfo());
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if (null != distPreExtExecutor) {

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.beforeExtExecuteStart(context,
							distPreExtExecutor);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			try {
				dbResultInfo.setDistPreResultInfo(distPreExtExecutor
						.execute(context));
			} catch (ExecutionException e) {
				dbResultInfo.addException(e);
				dbResultInfo.setEndTime(System.currentTimeMillis());
				return dbResultInfo;
			}

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.afterExtExecuteEnd(dbResultInfo
							.getDistPreResultInfo());
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		for (TableExecutor tableExecutor : tableExecutors) {

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.beforeTableExecuteStart(context,
							tableExecutor);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			TableResultInfo tableResultInfo = tableExecutor.execute(context);

			dbResultInfo.addTableResultInfo(tableResultInfo);

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.afterTableExecuteEnd(tableResultInfo);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if (null != distAfterExtExecutor) {

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.beforeExtExecuteStart(context,
							distAfterExtExecutor);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			try {
				dbResultInfo.setDistAfterResultInfo(distAfterExtExecutor
						.execute(context));
			} catch (ExecutionException e) {
				dbResultInfo.addException(e);
				dbResultInfo.setEndTime(System.currentTimeMillis());
				return dbResultInfo;
			}

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.afterExtExecuteEnd(dbResultInfo
							.getDistAfterResultInfo());
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if (null != sourceAfterExtExecutor) {

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.beforeExtExecuteStart(context,
							sourceAfterExtExecutor);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			try {
				dbResultInfo.setSourceAfterResultInfo(sourceAfterExtExecutor
						.execute(context));
			} catch (ExecutionException e) {
				dbResultInfo.addException(e);
				dbResultInfo.setEndTime(System.currentTimeMillis());
				return dbResultInfo;
			}

			try {
				for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
					dbExecutorListener.afterExtExecuteEnd(dbResultInfo
							.getSourceAfterResultInfo());
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		dbResultInfo.setEndTime(System.currentTimeMillis());

		try {
			for (IDBExecutorListener dbExecutorListener : dbExecutorListeners) {
				dbExecutorListener.afterDBExecuteEnd(dbResultInfo);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return dbResultInfo;
	}

	public DataSource getSourceDataSource() {
		return sourceDataSource;
	}

	public void setSourceDataSource(DataSource sourceDataSource) {
		this.sourceDataSource = sourceDataSource;
	}

	public DataSource getDistDataSource() {
		return distDataSource;
	}

	public void setDistDataSource(DataSource distDataSource) {
		this.distDataSource = distDataSource;
	}

	public List<TableExecutor> getTableExecutors() {
		return tableExecutors;
	}

	public void setTableExecutors(List<TableExecutor> tableExecutors) {
		this.tableExecutors = tableExecutors;
	}

	@Override
	public int getExtExecutorCount() {
		int count = 0;
		if (null != sourcePreExtExecutor) {
			count++;
		}

		if (null != distPreExtExecutor) {
			count++;
		}

		if (null != distAfterExtExecutor) {
			count++;
		}

		if (null != sourceAfterExtExecutor) {
			count++;
		}

		return count;
	}

	@Override
	public int getTableExecutorCount() {
		if (null != tableExecutors) {
			return tableExecutors.size();
		}

		return 0;
	}

	public void addListener(IDBExecutorListener dbExecutorListener) {
		if (!dbExecutorListeners.contains(dbExecutorListener)) {
			dbExecutorListeners.add(dbExecutorListener);
		}
	}

	public void removeListener(IDBExecutorListener dbExecutorListener) {
		if (dbExecutorListeners.contains(dbExecutorListener)) {
			dbExecutorListeners.remove(dbExecutorListener);
		}
	}
}
